// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/focus/focus_manager.h"

#include <algorithm>
#include <vector>

#include "base/auto_reset.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/focus/focus_manager_delegate.h"
#include "ui/views/focus/focus_search.h"
#include "ui/views/focus/view_storage.h"
#include "ui/views/focus/widget_focus_manager.h"
#include "ui/views/view.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace views {

bool FocusManager::arrow_key_traversal_enabled_ = false;

FocusManager::FocusManager(Widget* widget, FocusManagerDelegate* delegate)
    : widget_(widget)
    , delegate_(delegate)
    , focused_view_(NULL)
    , accelerator_manager_(new ui::AcceleratorManager)
    , shortcut_handling_suspended_(false)
    , focus_change_reason_(kReasonDirectFocusChange)
    , is_changing_focus_(false)
{
    DCHECK(widget_);
    stored_focused_view_storage_id_ = ViewStorage::GetInstance()->CreateStorageID();
}

FocusManager::~FocusManager()
{
}

bool FocusManager::OnKeyEvent(const ui::KeyEvent& event)
{
    const int key_code = event.key_code();

    if (event.type() != ui::ET_KEY_PRESSED && event.type() != ui::ET_KEY_RELEASED)
        return false;

    if (shortcut_handling_suspended())
        return true;

    ui::Accelerator accelerator(event);

    if (event.type() == ui::ET_KEY_PRESSED) {
        // If the focused view wants to process the key event as is, let it be.
        if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event) && !accelerator_manager_->HasPriorityHandler(accelerator))
            return true;

        // Intercept Tab related messages for focus traversal.
        // Note that we don't do focus traversal if the root window is not part of
        // the active window hierarchy as this would mean we have no focused view
        // and would focus the first focusable view.
        if (IsTabTraversalKeyEvent(event)) {
            AdvanceFocus(event.IsShiftDown());
            return false;
        }

        if (arrow_key_traversal_enabled_ && ProcessArrowKeyTraversal(event))
            return false;

        // Intercept arrow key messages to switch between grouped views.
        if (focused_view_ && focused_view_->GetGroup() != -1 && (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN || key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) {
            bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN);
            View::Views views;
            focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(),
                &views);
            View::Views::const_iterator i(
                std::find(views.begin(), views.end(), focused_view_));
            DCHECK(i != views.end());
            int index = static_cast<int>(i - views.begin());
            index += next ? 1 : -1;
            if (index < 0) {
                index = static_cast<int>(views.size()) - 1;
            } else if (index >= static_cast<int>(views.size())) {
                index = 0;
            }
            SetFocusedViewWithReason(views[index], kReasonFocusTraversal);
            return false;
        }
    }

    // Process keyboard accelerators.
    // If the key combination matches an accelerator, the accelerator is
    // triggered, otherwise the key event is processed as usual.
    if (ProcessAccelerator(accelerator)) {
        // If a shortcut was activated for this keydown message, do not propagate
        // the event further.
        return false;
    }
    return true;
}

void FocusManager::ValidateFocusedView()
{
    if (focused_view_ && !ContainsView(focused_view_))
        ClearFocus();
}

// Tests whether a view is valid, whether it still belongs to the window
// hierarchy of the FocusManager.
bool FocusManager::ContainsView(View* view)
{
    Widget* widget = view->GetWidget();
    return widget ? widget->GetFocusManager() == this : false;
}

void FocusManager::AdvanceFocus(bool reverse)
{
    View* v = GetNextFocusableView(focused_view_, NULL, reverse, false);
    // Note: Do not skip this next block when v == focused_view_.  If the user
    // tabs past the last focusable element in a webpage, we'll get here, and if
    // the TabContentsContainerView is the only focusable view (possible in
    // fullscreen mode), we need to run this block in order to cycle around to the
    // first element on the page.
    if (v) {
        views::View* focused_view = focused_view_;
        v->AboutToRequestFocusFromTabTraversal(reverse);
        // AboutToRequestFocusFromTabTraversal() may have changed focus. If it did,
        // don't change focus again.
        if (focused_view == focused_view_)
            SetFocusedViewWithReason(v, kReasonFocusTraversal);
    }
}

void FocusManager::ClearNativeFocus()
{
    // Keep the top root window focused so we get keyboard events.
    widget_->ClearNativeFocus();
}

bool FocusManager::RotatePaneFocus(Direction direction,
    FocusCycleWrappingBehavior wrap)
{
    // Get the list of all accessible panes.
    std::vector<View*> panes;
    widget_->widget_delegate()->GetAccessiblePanes(&panes);

    // Count the number of panes and set the default index if no pane
    // is initially focused.
    int count = static_cast<int>(panes.size());
    if (count == 0)
        return false;

    // Initialize |index| to an appropriate starting index if nothing is
    // focused initially.
    int index = direction == kBackward ? 0 : count - 1;

    // Check to see if a pane already has focus and update the index accordingly.
    const views::View* focused_view = GetFocusedView();
    if (focused_view) {
        for (int i = 0; i < count; i++) {
            if (panes[i] && panes[i]->Contains(focused_view)) {
                index = i;
                break;
            }
        }
    }

    // Rotate focus.
    int start_index = index;
    for (;;) {
        if (direction == kBackward)
            index--;
        else
            index++;

        if (wrap == kNoWrap && (index >= count || index < 0))
            return false;
        index = (index + count) % count;

        // Ensure that we don't loop more than once.
        if (index == start_index)
            break;

        views::View* pane = panes[index];
        DCHECK(pane);

        if (!pane->visible())
            continue;

        pane->RequestFocus();
        focused_view = GetFocusedView();
        if (pane == focused_view || pane->Contains(focused_view))
            return true;
    }

    return false;
}

View* FocusManager::GetNextFocusableView(View* original_starting_view,
    Widget* starting_widget,
    bool reverse,
    bool dont_loop)
{
    FocusTraversable* focus_traversable = NULL;

    // Let's revalidate the focused view.
    ValidateFocusedView();

    View* starting_view = NULL;
    if (original_starting_view) {
        // Search up the containment hierarchy to see if a view is acting as
        // a pane, and wants to implement its own focus traversable to keep
        // the focus trapped within that pane.
        View* pane_search = original_starting_view;
        while (pane_search) {
            focus_traversable = pane_search->GetPaneFocusTraversable();
            if (focus_traversable) {
                starting_view = original_starting_view;
                break;
            }
            pane_search = pane_search->parent();
        }

        if (!focus_traversable) {
            if (!reverse) {
                // If the starting view has a focus traversable, use it.
                // This is the case with NativeWidgetWins for example.
                focus_traversable = original_starting_view->GetFocusTraversable();

                // Otherwise default to the root view.
                if (!focus_traversable) {
                    focus_traversable = original_starting_view->GetWidget()->GetFocusTraversable();
                    starting_view = original_starting_view;
                }
            } else {
                // When you are going back, starting view's FocusTraversable
                // should not be used.
                focus_traversable = original_starting_view->GetWidget()->GetFocusTraversable();
                starting_view = original_starting_view;
            }
        }
    } else {
        Widget* widget = starting_widget ? starting_widget : widget_;
        focus_traversable = widget->GetFocusTraversable();
    }

    // Traverse the FocusTraversable tree down to find the focusable view.
    View* v = FindFocusableView(focus_traversable, starting_view, reverse);
    if (v) {
        return v;
    } else {
        // Let's go up in the FocusTraversable tree.
        FocusTraversable* parent_focus_traversable = focus_traversable->GetFocusTraversableParent();
        starting_view = focus_traversable->GetFocusTraversableParentView();
        while (parent_focus_traversable) {
            FocusTraversable* new_focus_traversable = NULL;
            View* new_starting_view = NULL;
            // When we are going backward, the parent view might gain the next focus.
            bool check_starting_view = reverse;
            v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(
                starting_view, reverse, FocusSearch::UP,
                check_starting_view, &new_focus_traversable, &new_starting_view);

            if (new_focus_traversable) {
                DCHECK(!v);

                // There is a FocusTraversable, traverse it down.
                v = FindFocusableView(new_focus_traversable, NULL, reverse);
            }

            if (v)
                return v;

            starting_view = focus_traversable->GetFocusTraversableParentView();
            parent_focus_traversable = parent_focus_traversable->GetFocusTraversableParent();
        }

        // If we get here, we have reached the end of the focus hierarchy, let's
        // loop. Make sure there was at least a view to start with, to prevent
        // infinitely looping in empty windows.
        if (!dont_loop && original_starting_view) {
            // Easy, just clear the selection and press tab again.
            // By calling with NULL as the starting view, we'll start from either
            // the starting views widget or |widget_|.
            Widget* widget = original_starting_view->GetWidget();
            if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget())
                widget = widget_;
            return GetNextFocusableView(NULL, widget, reverse, true);
        }
    }
    return NULL;
}

void FocusManager::SetFocusedViewWithReason(
    View* view, FocusChangeReason reason)
{
    if (focused_view_ == view)
        return;

    base::AutoReset<bool> auto_changing_focus(&is_changing_focus_, true);
    // Update the reason for the focus change (since this is checked by
    // some listeners), then notify all listeners.
    focus_change_reason_ = reason;
    FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
        OnWillChangeFocus(focused_view_, view));

    View* old_focused_view = focused_view_;
    focused_view_ = view;
    if (old_focused_view)
        old_focused_view->Blur();
    // Also make |focused_view_| the stored focus view. This way the stored focus
    // view is remembered if focus changes are requested prior to a show or while
    // hidden.
    SetStoredFocusView(focused_view_);
    if (focused_view_)
        focused_view_->Focus();

    FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
        OnDidChangeFocus(old_focused_view, focused_view_));
}

void FocusManager::ClearFocus()
{
    // SetFocusedView(NULL) is going to clear out the stored view to. We need to
    // persist it in this case.
    views::View* focused_view = GetStoredFocusView();
    SetFocusedView(NULL);
    ClearNativeFocus();
    SetStoredFocusView(focused_view);
}

void FocusManager::AdvanceFocusIfNecessary()
{
    // If widget is inactive, there is no focused view to check. The stored view
    // will also be checked for focusability when it is being restored.
    if (!widget_->IsActive())
        return;

    // If widget is active and focused view is not focusable, advance focus or,
    // if not possible, clear focus.
    if (focused_view_ && !focused_view_->IsAccessibilityFocusable()) {
        AdvanceFocus(false);
        if (focused_view_ && !focused_view_->IsAccessibilityFocusable())
            ClearFocus();
    }
}

void FocusManager::StoreFocusedView(bool clear_native_focus)
{
    View* focused_view = focused_view_;
    // Don't do anything if no focused view. Storing the view (which is NULL), in
    // this case, would clobber the view that was previously saved.
    if (!focused_view_)
        return;

    View* v = focused_view_;

    if (clear_native_focus) {
        // Temporarily disable notification.  ClearFocus() will set the focus to the
        // main browser window.  This extra focus bounce which happens during
        // deactivation can confuse registered WidgetFocusListeners, as the focus
        // is not changing due to a user-initiated event.
        AutoNativeNotificationDisabler local_notification_disabler;
        // ClearFocus() also stores the focused view.
        ClearFocus();
    } else {
        SetFocusedView(NULL);
        SetStoredFocusView(focused_view);
    }

    if (v)
        v->SchedulePaint(); // Remove focus border.
}

bool FocusManager::RestoreFocusedView()
{
    View* view = GetStoredFocusView();
    if (view) {
        if (ContainsView(view)) {
            if (!view->IsFocusable() && view->IsAccessibilityFocusable()) {
                // RequestFocus would fail, but we want to restore focus to controls
                // that had focus in accessibility mode.
                SetFocusedViewWithReason(view, kReasonFocusRestore);
            } else {
                // This usually just sets the focus if this view is focusable, but
                // let the view override RequestFocus if necessary.
                view->RequestFocus();

                // If it succeeded, the reason would be incorrect; set it to
                // focus restore.
                if (focused_view_ == view)
                    focus_change_reason_ = kReasonFocusRestore;
            }
        }
        return true;
    }
    return false;
}

void FocusManager::SetStoredFocusView(View* focus_view)
{
    ViewStorage* view_storage = ViewStorage::GetInstance();
    if (!view_storage) {
        // This should never happen but bug 981648 seems to indicate it could.
        NOTREACHED();
        return;
    }

    // TODO(jcivelli): when a TabContents containing a popup is closed, the focus
    // is stored twice causing an assert. We should find a better alternative than
    // removing the view from the storage explicitly.
    view_storage->RemoveView(stored_focused_view_storage_id_);

    if (!focus_view)
        return;

    view_storage->StoreView(stored_focused_view_storage_id_, focus_view);
}

View* FocusManager::GetStoredFocusView()
{
    ViewStorage* view_storage = ViewStorage::GetInstance();
    if (!view_storage) {
        // This should never happen but bug 981648 seems to indicate it could.
        NOTREACHED();
        return NULL;
    }

    return view_storage->RetrieveView(stored_focused_view_storage_id_);
}

void FocusManager::ClearStoredFocusedView()
{
    SetStoredFocusView(NULL);
}

// Find the next (previous if reverse is true) focusable view for the specified
// FocusTraversable, starting at the specified view, traversing down the
// FocusTraversable hierarchy.
View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
    View* starting_view,
    bool reverse)
{
    FocusTraversable* new_focus_traversable = NULL;
    View* new_starting_view = NULL;
    View* v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
        starting_view,
        reverse,
        FocusSearch::DOWN,
        false,
        &new_focus_traversable,
        &new_starting_view);

    // Let's go down the FocusTraversable tree as much as we can.
    while (new_focus_traversable) {
        DCHECK(!v);
        focus_traversable = new_focus_traversable;
        new_focus_traversable = NULL;
        starting_view = NULL;
        v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
            starting_view,
            reverse,
            FocusSearch::DOWN,
            false,
            &new_focus_traversable,
            &new_starting_view);
    }
    return v;
}

void FocusManager::RegisterAccelerator(
    const ui::Accelerator& accelerator,
    ui::AcceleratorManager::HandlerPriority priority,
    ui::AcceleratorTarget* target)
{
    accelerator_manager_->Register(accelerator, priority, target);
}

void FocusManager::UnregisterAccelerator(const ui::Accelerator& accelerator,
    ui::AcceleratorTarget* target)
{
    accelerator_manager_->Unregister(accelerator, target);
}

void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target)
{
    accelerator_manager_->UnregisterAll(target);
}

bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator)
{
    if (accelerator_manager_->Process(accelerator))
        return true;
    if (delegate_.get())
        return delegate_->ProcessAccelerator(accelerator);
    return false;
}

ui::AcceleratorTarget* FocusManager::GetCurrentTargetForAccelerator(
    const ui::Accelerator& accelerator) const
{
    ui::AcceleratorTarget* target = accelerator_manager_->GetCurrentTarget(accelerator);
    if (!target && delegate_.get())
        target = delegate_->GetCurrentTargetForAccelerator(accelerator);
    return target;
}

bool FocusManager::HasPriorityHandler(
    const ui::Accelerator& accelerator) const
{
    return accelerator_manager_->HasPriorityHandler(accelerator);
}

// static
bool FocusManager::IsTabTraversalKeyEvent(const ui::KeyEvent& key_event)
{
    return key_event.key_code() == ui::VKEY_TAB && !key_event.IsControlDown();
}

void FocusManager::ViewRemoved(View* removed)
{
    // If the view being removed contains (or is) the focused view,
    // clear the focus.  However, it's not safe to call ClearFocus()
    // (and in turn ClearNativeFocus()) here because ViewRemoved() can
    // be called while the top level widget is being destroyed.
    if (focused_view_ && removed->Contains(focused_view_))
        SetFocusedView(NULL);
}

void FocusManager::AddFocusChangeListener(FocusChangeListener* listener)
{
    focus_change_listeners_.AddObserver(listener);
}

void FocusManager::RemoveFocusChangeListener(FocusChangeListener* listener)
{
    focus_change_listeners_.RemoveObserver(listener);
}

bool FocusManager::ProcessArrowKeyTraversal(const ui::KeyEvent& event)
{
    if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
        return false;

    const int key_code = event.key_code();
    if (key_code == ui::VKEY_LEFT || key_code == ui::VKEY_UP) {
        AdvanceFocus(true);
        return true;
    }
    if (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN) {
        AdvanceFocus(false);
        return true;
    }

    return false;
}

} // namespace views
